package main

import (
	"context"
	"fmt"
	"gitee.com/ymofen/interval"
	"gitee.com/ymofen/interval/internal"
	"log"
	"math/rand"
	"sync/atomic"
	"time"
)

var logPrintf = func(s string, args ...interface{}) {
	log.Printf(s, args...)
}

type intervalCheck struct {
	succ      int
	errn      int
	sn        atomic.Int32
	id        string
	logFlag   int8
	intv      time.Duration
	preTime   time.Time
	startTime time.Time
}

func (ic *intervalCheck) doRandCheck() {
	if !ic.preTime.IsZero() {
		nowTime := time.Now()
		av := nowTime.Sub(ic.preTime)
		dv := av - ic.intv
		if dv < 0 || dv > time.Millisecond*100 { // 允许100毫秒误差
			logPrintf("%s execute error, now:%v sn:%d,  dv/av:%v != %v", ic.id, time.Now().Format(time.RFC3339Nano), ic.sn.Load(), dv, av)
			ic.errn++
		} else {
			if ic.logFlag == 1 {
				logPrintf("%s execute, now:%v sn:%d,  dv/av:%v/%v", ic.id, time.Now().Format(time.RFC3339Nano), ic.sn.Load(), dv, av)
			}
			ic.succ++
		}
	} else {
		logPrintf("%s execute, preTime is zero, now:%v, sn:%d, intv:%v", ic.id, time.Now().Format(time.RFC3339Nano), ic.sn.Load(), ic.intv)
	}
	ic.preTime = time.Now()
}

func (ic *intervalCheck) start() {
	ic.startTime = time.Now()
	interval.OpenInterval(ic.id, ic.intv, func() {
		ic.sn.Add(1)
		ic.doRandCheck()
	})
}

func (ic *intervalCheck) status() string {
	av := time.Since(ic.startTime)
	cnt := ic.sn.Load()
	var dv time.Duration
	if cnt == 0 {
		dv = 0
	} else {
		exceptDuration := time.Second + time.Duration(cnt-1)*ic.intv
		dv = av - exceptDuration
	}
	return fmt.Sprintf("%s\tsn=%d&dv=%v&stat=%d/%d", ic.id, ic.sn.Load(), dv, ic.succ, ic.errn)
}

type fixedIntervalCheck struct {
	succ      int
	errn      int
	iv        *interval.IntervalListEngine
	logFlag   int8
	sn        atomic.Int32
	id        string
	intv      time.Duration
	preTime   time.Time
	startTime time.Time
}

func (ic *fixedIntervalCheck) doRandCheck() {
	if !ic.preTime.IsZero() {
		nowTime := time.Now()
		av := nowTime.Sub(ic.preTime)
		dv := av - ic.intv
		if dv > time.Millisecond*100 { // 允许100毫秒误差, 可能提前一点
			logPrintf("%s execute error, now:%v sn:%d,  dv/av:%v != %v", ic.id, time.Now().Format(time.RFC3339Nano), ic.sn.Load(), dv, av)
			ic.errn++
		} else {
			mustAt := interval.RoundIntervalAt(nowTime, ic.startTime, ic.intv)
			if nowTime.Sub(mustAt).Abs() > time.Millisecond*100 {
				logPrintf("%s execute error, now:%v sn:%d,  runAt:%v, mustAt:%v, dv/av:%v/%v", ic.id, time.Now().Format(time.RFC3339Nano), ic.sn.Load(), nowTime.Format(time.RFC3339Nano), mustAt.Format(time.RFC3339Nano), dv, av)
				ic.errn++
			} else {
				ic.succ++
				if ic.logFlag == 1 {
					logPrintf("%s execute, now:%v sn:%d,  runAt:%v, mustAt:%v, dv/av:%v/%v", ic.id, time.Now().Format(time.RFC3339Nano), ic.sn.Load(), nowTime.Format(time.RFC3339Nano), mustAt.Format(time.RFC3339Nano), dv, av)
				}

			}
		}
	} else {
		logPrintf("%s execute, preTime is zero, now:%v, sn:%d, intv:%v", ic.id, time.Now().Format(time.RFC3339Nano), ic.sn.Load(), ic.intv)
	}
	ic.preTime = time.Now()
}

func (ic *fixedIntervalCheck) start() {
	ic.startTime = time.Now().Truncate(time.Second)
	ic.iv.OpenIntervalWithNextRunAt(ic.id, ic.startTime, ic.intv, func() {
		ic.sn.Add(1)
		ic.doRandCheck()
	})
}

func (ic *fixedIntervalCheck) status() string {
	av := time.Since(ic.startTime)
	cnt := ic.sn.Load()
	var dv time.Duration
	if cnt == 0 {
		dv = 0
	} else {
		exceptDuration := time.Second + time.Duration(cnt-1)*ic.intv
		dv = av - exceptDuration
	}
	return fmt.Sprintf("%s\tsn=%d&dv=%v&stat=%d/%d", ic.id, ic.sn.Load(), dv, ic.succ, ic.errn)
}

func main() {
	interval.SlowDebugf = logPrintf

	type statusFunc = func() string
	var statusFuncs = make([]statusFunc, 0)

	{
		var tasklst = []struct {
			id      string
			intv    time.Duration
			logFlag int8
		}{
			{id: "ms500", intv: time.Millisecond * 500},
			{id: "s01", intv: time.Second},
			{id: "s10", intv: time.Second * 10},
			{id: "s30", intv: time.Second * 30},
			{id: "s60", intv: time.Second * 60, logFlag: 1},
			{id: "m05", intv: time.Minute * 5, logFlag: 1},
			{id: "h01", intv: time.Hour * 1, logFlag: 1},
			{id: "h12", intv: time.Hour * 12, logFlag: 1},
			{id: "day01", intv: time.Hour * 24, logFlag: 1},
			{id: "day07", intv: time.Hour * 24 * 7, logFlag: 1},
			{id: "day30", intv: time.Hour * 24 * 30, logFlag: 1},
		}
		for _, ts := range tasklst {
			ic := &intervalCheck{id: ts.id, intv: ts.intv, logFlag: ts.logFlag}
			ic.start()
			statusFuncs = append(statusFuncs, ic.status)
		}
	}

	ive := interval.NewIntervalListEngine()
	{
		var tasklst = []struct {
			id      string
			intv    time.Duration
			logFlag int8
		}{
			{id: "fixed-ms500", intv: time.Millisecond * 500},
			{id: "fixed-s01", intv: time.Second},
			{id: "fixed-s10", intv: time.Second * 10},
			{id: "fixed-s30", intv: time.Second * 30},
			{id: "fixed-s60", intv: time.Second * 60, logFlag: 1},
			{id: "fixed-m05", intv: time.Minute * 5, logFlag: 1},
			{id: "fixed-h01", intv: time.Hour * 1, logFlag: 1},
			{id: "fixed-h12", intv: time.Hour * 12, logFlag: 1},
			{id: "fixed-day01", intv: time.Hour * 24, logFlag: 1},
			{id: "fixed-day07", intv: time.Hour * 24 * 7, logFlag: 1},
			{id: "fixed-day30", intv: time.Hour * 24 * 30, logFlag: 1},
		}

		for _, ts := range tasklst {
			ic := &fixedIntervalCheck{id: ts.id, iv: ive, intv: ts.intv, logFlag: ts.logFlag}
			ic.start()
			statusFuncs = append(statusFuncs, ic.status)
		}
	}

	now := time.Now()
	{
		nextHour := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
		if nextHour.Before(now) {
			nextHour = nextHour.Add(time.Hour)
		}
		logPrintf("nextHour startup at:%v", nextHour.Format(time.RFC3339Nano))
		preTime := time.Now()
		ive.OpenIntervalWithNextRunAt("nextHour", nextHour, time.Hour, func() {
			logPrintf("nextHour execute, now:%v, dv:%v", time.Now().Format(time.RFC3339Nano), time.Since(preTime))
			preTime = time.Now()
		})
	}

	{
		nextTime := time.Now().Truncate(time.Minute * 10)
		if nextTime.Before(now) {
			nextTime = nextTime.Add(time.Minute * 10)
		}
		logPrintf("nextMinute10 startup at:%v", nextTime.Format(time.RFC3339Nano))
		preTime := time.Now()
		ive.OpenIntervalWithNextRunAt("next_m10", nextTime, time.Minute*10, func() {
			logPrintf("nextMinute10 execute, now:%v,  dv:%v", time.Now().Format(time.RFC3339Nano), time.Since(preTime))
			preTime = time.Now()
		})
	}

	{
		preTime := time.Now()
		ive.OpenInterval("ive_m15", time.Minute*15, func() {
			logPrintf("ive m15 execute, now:%v,  dv:%v", time.Now().Format(time.RFC3339Nano), time.Since(preTime))
			preTime = time.Now()
		})
	}

	{
		var preTime time.Time
		var intv time.Duration
		var sn atomic.Int32
		var ivId = "ive_rand"
		var randIntv = func() {
			intv = time.Minute*3 + time.Duration(rand.Intn(1000*60))*time.Millisecond
		}
		randIntv()
		var doRandCheck = func() {
			if !preTime.IsZero() {
				nowTime := time.Now()
				av := nowTime.Sub(preTime)
				dv := av - intv
				if dv < 0 || dv > time.Millisecond*100 { // 允许100毫秒误差
					logPrintf("ive_rand execute error, now:%v sn:%d,  dv/av:%v != %v", time.Now().Format(time.RFC3339Nano), sn.Load(), dv, av)
				} else {
					logPrintf("ive_rand execute, now:%v, sn:%d, av:%v,  dv:%v", time.Now().Format(time.RFC3339Nano), sn.Load(), av, dv)
				}
			} else {
				logPrintf("ive_rand execute, preTime is zero, now:%v, sn:%d, intv:%v", time.Now().Format(time.RFC3339Nano), sn.Load(), intv)
			}
			preTime = time.Now()
			logPrintf("ive_rand set preTime=%v, sn:%d", preTime.Format(time.RFC3339Nano), sn.Load())
		}

		var intvFunc func()
		intvFunc = func() {
			doRandCheck()
			sn.Add(1)
			if sn.Load()%3 == 0 {
				randIntv()
				logPrintf("ive_rand update, now:%v, sn:%d, intv:%v",
					time.Now().Format(time.RFC3339Nano), sn.Load(), intv)

				ive.OpenInterval(ivId, intv, intvFunc)
				if sn.Load()%6 == 0 {
					logPrintf("ive_rand closeInterval, now:%v, sn:%d, intv:%v",
						time.Now().Format(time.RFC3339Nano), sn.Load(), intv)
					ive.CloseInterval(ive)
					preTime = time.Time{}
					time.AfterFunc(time.Duration(rand.Intn(1000))*time.Millisecond, func() {
						randIntv()
						logPrintf("ive_rand reopen, now:%v, sn:%d, intv:%v",
							time.Now().Format(time.RFC3339Nano), sn.Load(), intv)
						ive.OpenInterval(ivId, intv, intvFunc)
					})
				}
			}
		}

		ive.OpenInterval(ivId, intv, intvFunc)
	}

	{
		nextTime := time.Now().Truncate(time.Hour * 36)
		if nextTime.Before(now) {
			nextTime = nextTime.Add(time.Hour * 36)
		}
		logPrintf("nextHour36 startup at:%v", nextTime.Format(time.RFC3339Nano))
		preTime := time.Now()
		ive.OpenIntervalWithNextRunAt("nextHour36", nextTime, time.Hour, func() {
			logPrintf("nextHour36 execute, now:%v, dv:%v", time.Now().Format(time.RFC3339Nano), time.Since(preTime))
			preTime = time.Now()
		})
	}

	time.Sleep(time.Second)
	//interval.OpenInterval("status", time.Second*300, func() {
	//	var bb strings.Builder
	//	for _, sf := range statusFuncs {
	//		bb.WriteString(sf())
	//		bb.WriteString("\t")
	//	}
	//	logPrintf("status %s", bb.String())
	//})

	internal.RunContextAndInterval(context.Background(), time.Second*300, func() {
		for _, sf := range statusFuncs {
			logPrintf("status %s", sf())
		}

		//var bb strings.Builder
		//for _, sf := range statusFuncs {
		//	bb.WriteString(sf())
		//	bb.WriteString("\t")
		//}
		//logPrintf("status %s", bb.String())
	})
}
